package com.hjm.common.utils;



import com.hjm.common.utils.trade.DateUtil;
import com.hjm.common.utils.trade.LoFunction;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.Security;
import java.security.spec.KeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class EncyptorUtil {
    private LoFunction loFunction = new LoFunction();
    /**
     * SM4算法目前只支持128位（即密钥16字节）
     */
    private static final int DEFAULT_KEY_SIZE = 128;
    private static final String ALGORITHM_NAME = "SM4";
    private static final String ALGORITHM_ECB_PKCS5PADDING = "SM4/ECB/NoPadding";

    private static final String CODEING="utf-8";
    private static final String KEY_ALGORITHM = "AES";
    //默认偏移量，AES 128位数据块对应偏移量为16位
    public static final String OFFSET = "1234567890abcdef";
    //AES：加密方式   CBC：工作模式   PKCS5Padding：填充模
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";



    static {
        // 防止内存中出现多次BouncyCastleProvider的实例
        if (null == Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    /**
     *
     ********************************************************* .<br>
     * [方法] desEncrypt <br>
     * [描述] 3des加密 <br>
     * [参数] mab,key <br>
     * [返回] byte[] <br>
     * [时间] 2014-11-17 下午2:04:31 <br>
     ********************************************************* .<br>
     */
    public  String desEncrypt(String data, String key) throws Exception{
        byte newkey[] = new byte[24];
        System.arraycopy(key, 0, newkey, 0, loFunction.hexStr2Bytes(key).length);
        System.arraycopy(key, 0, newkey, 16, 8);
        Key k = toKey(newkey); // ---这里卡了一下、百度一下可以优化吗
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, k);
        return loFunction.byte2HexStr(cipher.doFinal(loFunction.hexStr2Bytes(data)));
    }


    /**
     * 3Des解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public  String desDecrypt(String data, String key) throws Exception{
        byte newkey[] = new byte[24];
        System.arraycopy(key, 0, newkey, 0, loFunction.hexStr2Bytes(key).length);
        System.arraycopy(key, 0, newkey, 16, 8);
        Key k = toKey(newkey);
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, k);
        return loFunction.byte2HexStr(cipher.doFinal(loFunction.hexStr2Bytes(data)));
    }

    /**
     *
     ********************************************************* .<br>
     * [方法] desEncrypt <br>
     * [描述] 3des加密 <br>
     * [参数] mab,key <br>
     * [返回] byte[] <br>
     * [时间] 2014-11-17 下午2:04:31 <br>
     ********************************************************* .<br>
     */
    public  String desEncrypt(byte[] data, byte[] key) throws Exception{
        byte newkey[] = new byte[24];
        System.arraycopy(key, 0, newkey, 0, key.length);
        System.arraycopy(key, 0, newkey, 16, 8);
        Key k = toKey(newkey); // ---这里卡了一下、百度一下可以优化吗
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, k);
        return loFunction.byte2HexStr(cipher.doFinal(data));
    }


    /**
     * 3Des解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public  String desDecrypt(byte[] data, byte[] key) throws Exception{
        byte newkey[] = new byte[24];
        System.arraycopy(key, 0, newkey, 0, key.length);
        System.arraycopy(key, 0, newkey, 16, 8);
        Key k = toKey(newkey);
        Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, k);
        return loFunction.byte2HexStr(cipher.doFinal(data));
    }

    private SecretKey toKey(byte[] key) throws Exception {
        KeySpec dks = new DESedeKeySpec(key); // 这里改为了 DESedeKeySpec
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
        return keyFactory.generateSecret(dks);
    }


    /**
     * 加密，SM4-ECB-PKCS5Padding
     *
     * @param date 要加密的明文
     * @return 加密后的密文
     * @throws Exception 加密异常
     */
    public String sm4EncryptECB(String SM4_KEY,String date) throws Exception {
        return loFunction.byte2HexStr(sm4(loFunction.hexStr2Bytes(date), loFunction.hexStr2Bytes(SM4_KEY), Cipher.ENCRYPT_MODE));
    }

    /**
     * 加密，SM4-ECB-PKCS5Padding
     *
     * @param data 要加密的明文
     * @return 加密后的密文
     * @throws Exception 加密异常
     */
    public  byte[] sm4EncryptECB(String SM4_KEY,byte[] data) throws Exception {
        return sm4(data, loFunction.hexStr2Bytes(SM4_KEY), Cipher.ENCRYPT_MODE);
    }

    /**
     * 解密，SM4-ECB-PKCS5Padding
     *
     * @param data 要解密的密文
     * @return 解密后的明文
     * @throws Exception 解密异常
     */
    public  byte[] sm4DecryptECB(String SM4_KEY,byte[] data) throws Exception {
        return sm4(data, loFunction.hexStr2Bytes(SM4_KEY), Cipher.DECRYPT_MODE);
    }

    /**
     * SM4对称加解密
     *
     * @param input 明文（加密模式）或密文（解密模式）
     * @param key   密钥
     * @param mode  Cipher.ENCRYPT_MODE - 加密；Cipher.DECRYPT_MODE - 解密
     * @return 密文（加密模式）或明文（解密模式）
     * @throws Exception 加解密异常
     */
    private  byte[] sm4(byte[] input, byte[] key, int mode)
            throws Exception {
        SecretKeySpec sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        Cipher cipher = Cipher
                .getInstance(ALGORITHM_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(mode, sm4Key);
        return cipher.doFinal(input);
    }

    /**
     * 功能描述: aes加密
     */
    public  String aesEncrypt(String data, String key,String... offset) {
        try {
            key = key.substring(0, 16);
            //偏移量
            byte[] offsetByte = offset.length > 0 ? offset[0].getBytes(CODEING) : OFFSET.getBytes(CODEING);
            IvParameterSpec zeroIv = new IvParameterSpec(offsetByte);
            byte[] raw = key.getBytes(CODEING);
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,zeroIv);
            byte[] encrypted = cipher.doFinal(data.getBytes("utf-8"));
            return loFunction.byte2HexStr(encrypted);
        } catch (Exception ex) {
           ex.getMessage();
        }
        return null;
    }



    /**
     * 功能描述: aes解密
     */
    public  String aesDecrypt(String data, String key,String... offset) {
        try {
            key = key.substring(0, 16);
            //偏移量
            byte[] offsetByte = offset.length > 0 ? offset[0].getBytes(CODEING) : OFFSET.getBytes(CODEING);
            IvParameterSpec zeroIv = new IvParameterSpec(offsetByte);
            byte[] raw = key.getBytes(CODEING);
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,zeroIv);
            byte[] encrypted1 = loFunction.hexStr2Bytes(data);
            byte[] original = cipher.doFinal(encrypted1);
            return new String(original, CODEING);
        } catch (Exception ex) {
            ex.getMessage();
        }
        return null;
    }

    /**
     * 生产32位随机密钥
     * @param length
     * @return
     */
    public  String getRandomString(int length){
        String str="ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    /**
     *生成3des 校验值
     * @param key
     * @return
     */

    public  String getDesCkKey(String key){
        String syskey = "0000000000000000";
        String ckKey = "0000000000000000";
        try {
            ckKey = desEncrypt(loFunction.hexStr2Bytes(syskey),loFunction.hexStr2Bytes(key));
        }catch (Exception  e){
            e.getMessage();
        }
        return ckKey.substring(0, 16);
    }

    /**
     * 生成SM4算法校验值
     * @param key
     * @return
     */
    public  String getSM4CkKey(String key){
        String syskey = "00000000000000000000000000000000";
        String ckKey = "0000000000000000";
        try {
            ckKey = loFunction.byte2HexStr(sm4EncryptECB(key,loFunction.hexStr2Bytes(syskey)));
        }catch (Exception  e){
            e.getMessage();
        }
        return ckKey.substring(0, 16);
    }

    /**
     * 生成3DES工作密钥
     * @param TMk
     * @return
     */
    public Map<String, String> getDesWorkKey(String TMk) throws Exception {
        /**
         * MACKEY
         */
        String origMacKey = getRandomString(32);//生成macKey
        String macKeyTmk = desEncrypt(origMacKey,TMk);//终端主密钥加密的macKey返给终端
        String macKeyCkv = getDesCkKey(origMacKey);//存库
        /**
         * PINKEY
         */
        String origPinKey = getRandomString(32);//生成pinKey
        String pinKeyTmk = desEncrypt(origPinKey,TMk);//终端主密钥加密的pinKey返给终端
        String pinKeyCkv = getDesCkKey(origPinKey);//存库
        /**
         * TDkKey
         */
        String origTdkKey = getRandomString(32);//生成随机字符串 0 ~ F
        String TdkKey = desEncrypt(origTdkKey,TMk);//密文pinkey
        String TdkKeyCkv = getDesCkKey(origTdkKey);//pin校验值

        String F62 = DateUtil.appendField(pinKeyTmk,pinKeyCkv,macKeyTmk,macKeyCkv,TdkKey,TdkKeyCkv);
        Map<String,String> wokkey = new HashMap<String,String>();
        wokkey.put("origMacKey",origMacKey);
        wokkey.put("macKeyTmk",macKeyTmk);
        wokkey.put("macKeyCkv",macKeyCkv);
        wokkey.put("origPinKey",origPinKey);
        wokkey.put("pinKeyTmk",pinKeyTmk);
        wokkey.put("pinKeyCkv",pinKeyCkv);
        wokkey.put("origTdkKey",origTdkKey);
        wokkey.put("TdkKey",TdkKey);
        wokkey.put("TdkKeyCkv",TdkKeyCkv);
        wokkey.put("workKey",F62);
        return wokkey;
    }

    /**
     * 生成SM4工作密钥
     * @param TMk
     * @return
     */
    public Map<String, String> getSM4WorkKey(String TMk) throws Exception{
        /**
         * MACKEY
         */
        String origMacKey = getRandomString(32);//生成macKey
        String macKeyTmk = sm4EncryptECB(TMk,origMacKey);//终端主密钥加密的macKey返给终端
        String macKeyCkv = getSM4CkKey(origMacKey);//存库
        /**
         * PINKEY
         */
        String origPinKey = getRandomString(32);//生成pinKey
        String pinKeyTmk = sm4EncryptECB(TMk,origPinKey);//终端主密钥加密的pinKey返给终端
        String pinKeyCkv = getSM4CkKey(origPinKey);//存库
        /**
         * TDkKey
         */
        String origTdkKey = getRandomString(32);//生成随机字符串 0 ~ F
        String TdkKey = sm4EncryptECB(TMk,origTdkKey);//密文pinkey
        String TdkKeyCkv = getSM4CkKey(origTdkKey);//pin校验值

        String F62 = DateUtil.appendField(pinKeyTmk,pinKeyCkv,macKeyTmk,macKeyCkv,TdkKey,TdkKeyCkv);
        Map<String,String> wokkey = new HashMap<String,String>();
        wokkey.put("origMacKey",origMacKey);
        wokkey.put("macKeyTmk",macKeyTmk);
        wokkey.put("macKeyCkv",macKeyCkv);
        wokkey.put("origPinKey",origPinKey);
        wokkey.put("pinKeyTmk",pinKeyTmk);
        wokkey.put("pinKeyCkv",pinKeyCkv);
        wokkey.put("origTdkKey",origTdkKey);
        wokkey.put("TdkKey",TdkKey);
        wokkey.put("TdkKeyCkv",TdkKeyCkv);
        wokkey.put("workKey",F62);
        return wokkey;
    }

    public static void main(String[] args) throws Exception {
        EncyptorUtil encyptorUtil = new EncyptorUtil();
        String TMk= encyptorUtil.getRandomString(32);
        System.out.println(TMk);
        String ck = encyptorUtil.getSM4CkKey(TMk);
        System.out.println(ck);
        System.out.println(encyptorUtil.getSM4WorkKey(TMk));


    }

    /**
     * ******************************************************** .<br>
     * [方法] mabXOR <br>
     * [描述] mab异或 <br>
     * [参数] mab <br>
     * [返回] byte[] <br>
     * [时间] 2015-12-10 上午9:24:08 <br>
     * ******************************************************** .<br>
     */
    public byte[] mabXOR(byte[] mab) throws Exception {
        int length = mab.length;
        int x = length % 8;
        int addLen = 0;
        addLen = (x != 0) ? (8 - length % 8) : addLen;
        byte[] data = new byte[length + addLen];
        System.arraycopy(mab, 0, data, 0, length);
        System.arraycopy(new byte[addLen], 0, data, mab.length, addLen);
        int pos = 0;
        byte[] block1 = new byte[8];
        System.arraycopy(data, pos, block1, 0, 8);
        pos += 8;
        for (int i = 1; i < data.length / 8; i++) {
            byte[] block2 = new byte[8];
            System.arraycopy(data, pos, block2, 0, 8);
            byte[] t = bytesXOR(block1, block2);
            block1 = t;
            pos += 8;
        }
        //System.out.println("最后异或"+new LoFunction().byte2HexStr(block1));
        return block1;
    }

    /**
     * ******************************************************** .<br>
     * [方法] bytesXOR <br>
     * [描述] 字节数组异或 <br>
     * [参数] 数据1、数据2 <br>
     * [返回] byte[] <br>
     * [时间] 2015-12-9 下午5:21:22 <br>
     * ******************************************************** .<br>
     */
    public byte[] bytesXOR(byte[] src, byte[] src1) throws Exception {
        int length = src.length;
        if (length != src1.length) {
            return null;
        }
        byte[] result = new byte[length];
        for (int i = 0; i < length; i++) {
            result[i] = byteXOR(src[i], src1[i]);
        }
        return result;
    }
    /**
     * ******************************************************** .<br>
     * [方法] byteXOR <br>
     * [描述] 字节异或 <br>
     * [参数] 数据1、数据2 <br>
     * [返回] byte <br>
     * [时间] 2015-12-9 下午5:21:11 <br>
     * ******************************************************** .<br>
     */
    private byte byteXOR(byte src, byte src1) throws Exception {
        return (byte) ((src & 0xFF) ^ (src1 & 0xFF));
    }


}
